package wci.frontend.pascal.parsers;

import static wci.frontend.pascal.PascalErrorCode.IDENTIFIER_UNDEFINED;
import static wci.frontend.pascal.PascalErrorCode.MISSING_RIGHT_PAREN;
import static wci.frontend.pascal.PascalErrorCode.UNEXPECTED_TOKEN;
import static wci.frontend.pascal.PascalTokenType.DIV;
import static wci.frontend.pascal.PascalTokenType.EQUALS;
import static wci.frontend.pascal.PascalTokenType.GREATER_EQUALS;
import static wci.frontend.pascal.PascalTokenType.GREATER_THAN;
import static wci.frontend.pascal.PascalTokenType.LESS_EQUALS;
import static wci.frontend.pascal.PascalTokenType.LESS_THAN;
import static wci.frontend.pascal.PascalTokenType.MINUS;
import static wci.frontend.pascal.PascalTokenType.NOT_EQUALS;
import static wci.frontend.pascal.PascalTokenType.PLUS;
import static wci.frontend.pascal.PascalTokenType.RIGHT_PAREN;
import static wci.frontend.pascal.PascalTokenType.SLASH;
import static wci.frontend.pascal.PascalTokenType.STAR;
import static wci.intermediate.icodeimpl.ICodeKeyImpl.ID;
import static wci.intermediate.icodeimpl.ICodeKeyImpl.VALUE;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.ADD;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.EQ;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.FLOAT_DIVIDE;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.GE;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.GT;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.INTEGER_CONSTANT;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.INTEGER_DIVIDE;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.LE;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.LT;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.MULTIPLY;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.NE;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.NEGATE;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.REAL_CONSTANT;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.STRING_CONSTANT;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.SUBTRACT;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.VARIABLE;

import java.util.EnumSet;
import java.util.HashMap;

import wci.frontend.Token;
import wci.frontend.TokenType;
import wci.frontend.pascal.PascalParserTD;
import wci.frontend.pascal.PascalTokenType;
import wci.intermediate.ICodeFactory;
import wci.intermediate.ICodeNode;
import wci.intermediate.ICodeNodeType;
import wci.intermediate.SymTabEntry;
import wci.intermediate.icodeimpl.ICodeNodeTypeImpl;

/**
 * <h1>ExpressionParser</h1>
 *
 * <p>解析表达式，目前表达式较为简单</p>
 *
 * <p>Copyright (c) 2009 by Ronald Mak</p>
 * <p>For instructional purposes only.  No warranties.</p>
 */
public class ExpressionParser extends StatementParser
{
    public ExpressionParser(PascalParserTD parent)
    {
        super(parent);
    }

    /**
     * 解析一个表达式，如语法图中的expression/term/factor等
     * @param token 表达式第一个token
     * @return 表达式分析子树根节点
     * @throws Exception
     */
    public ICodeNode parse(Token token)
        throws Exception
    {
        return parseExpression(token);
    }

    // 关系操作符集合
    private static final EnumSet<PascalTokenType> REL_OPS =
        EnumSet.of(EQUALS, NOT_EQUALS, LESS_THAN, LESS_EQUALS,
                   GREATER_THAN, GREATER_EQUALS);

    // Token操作符与分析树节点的映射
    private static final HashMap<PascalTokenType, ICodeNodeType>
        REL_OPS_MAP = new HashMap<PascalTokenType, ICodeNodeType>();
    static {
        REL_OPS_MAP.put(EQUALS, EQ);
        REL_OPS_MAP.put(NOT_EQUALS, NE);
        REL_OPS_MAP.put(LESS_THAN, LT);
        REL_OPS_MAP.put(LESS_EQUALS, LE);
        REL_OPS_MAP.put(GREATER_THAN, GT);
        REL_OPS_MAP.put(GREATER_EQUALS, GE);
    };

    /**
     * @param token 初始token
     * @return 表达式分析子树根节点
     * @throws Exception if an error occurred.
     */
    private ICodeNode parseExpression(Token token)
        throws Exception
    {
        //参见语法图5-2的语法规则，首先是一个simpleExpression，如果有关系
    	//操作符，还有一个用来比较的simpleExpression
        ICodeNode rootNode = parseSimpleExpression(token);
        token = currentToken();
        TokenType tokenType = token.getType();

        // 有比较
        if (REL_OPS.contains(tokenType)) {

            // 如果有比较操作符，则比较符是根节点，两个simpleExpression分别作为左右子节点
        	//比如 a > b，那么分析树为  >
        	//                 / \
        	//                a   b
            ICodeNodeType nodeType = REL_OPS_MAP.get(tokenType);
            ICodeNode opNode = ICodeFactory.createICodeNode(nodeType);
            opNode.addChild(rootNode);
            //跳过比较符
            token = nextToken();  
            opNode.addChild(parseSimpleExpression(token));
            rootNode = opNode;
        }

        return rootNode;
    }

    // 加法类运算符
    private static final EnumSet<PascalTokenType> ADD_OPS =
        EnumSet.of(PLUS, MINUS, PascalTokenType.OR);

    // 加法类运算符toke和对应分析树节点之间的映射
    private static final HashMap<PascalTokenType, ICodeNodeTypeImpl>
        ADD_OPS_OPS_MAP = new HashMap<PascalTokenType, ICodeNodeTypeImpl>();
    static {
        ADD_OPS_OPS_MAP.put(PLUS, ADD);
        ADD_OPS_OPS_MAP.put(MINUS, SUBTRACT);
        ADD_OPS_OPS_MAP.put(PascalTokenType.OR, ICodeNodeTypeImpl.OR);
    };

    /**
     * 解析一个简单表达式
     * @param token 表达式第一个token
     * @return 简单表达式分析子树根节点
     * @throws Exception 
     */
    private ICodeNode parseSimpleExpression(Token token)
        throws Exception
    {
    	//参见语法图5-2 的语法规则
        TokenType signType = null; 
        //探测前面可选的正/负 符号
        TokenType tokenType = token.getType();
        if ((tokenType == PLUS) || (tokenType == MINUS)) {
            signType = tokenType;
            token = nextToken(); 
        }
        // 紧接着的一个term
        ICodeNode rootNode = parseTerm(token);
        // 正号忽略掉，负号须创建一个表示反值运算的"NEGATE”节点
        if (signType == MINUS) {
            ICodeNode negateNode = ICodeFactory.createICodeNode(NEGATE);
            negateNode.addChild(rootNode);
            rootNode = negateNode;
        }
        
        token = currentToken();
        tokenType = token.getType();

        // 探测有没有加法类运算，如果有，加法类运算符后面还有另一个term,并以运算符作为根节点。
        // 如果类似于EBNF语法 (OP TERM)+的多个，请留意它根节点的变化，是从右到左的。
        while (ADD_OPS.contains(tokenType)) {
            ICodeNodeType nodeType = ADD_OPS_OPS_MAP.get(tokenType);
            ICodeNode opNode = ICodeFactory.createICodeNode(nodeType);
            opNode.addChild(rootNode);
            //跳过运算符
            token = nextToken(); 
            // 另一个term
            opNode.addChild(parseTerm(token));
            rootNode = opNode;

            token = currentToken();
            tokenType = token.getType();
        }

        return rootNode;
    }
    // 乘法类运算符
    private static final EnumSet<PascalTokenType> MULT_OPS =
        EnumSet.of(STAR, SLASH, DIV, PascalTokenType.MOD, PascalTokenType.AND);
    //惩罚类运算符与分析树节点映射
    private static final HashMap<PascalTokenType, ICodeNodeType>
        MULT_OPS_OPS_MAP = new HashMap<PascalTokenType, ICodeNodeType>();
    static {
        MULT_OPS_OPS_MAP.put(STAR, MULTIPLY);
        MULT_OPS_OPS_MAP.put(SLASH, FLOAT_DIVIDE);
        MULT_OPS_OPS_MAP.put(DIV, INTEGER_DIVIDE);
        MULT_OPS_OPS_MAP.put(PascalTokenType.MOD, ICodeNodeTypeImpl.MOD);
        MULT_OPS_OPS_MAP.put(PascalTokenType.AND, ICodeNodeTypeImpl.AND);
    };

    /**
     * 解析一个term
     * @param token term首token
     * @return term分析子树根节点
     * @throws Exception
     */
    private ICodeNode parseTerm(Token token)
        throws Exception
    {
        //请对照语法图5-2规则看代码
        ICodeNode rootNode = parseFactor(token);

        token = currentToken();
        TokenType tokenType = token.getType();

        //(OP FACTOR)+，与解析简单表达式后的term基本一样
        while (MULT_OPS.contains(tokenType)) {
            ICodeNodeType nodeType = MULT_OPS_OPS_MAP.get(tokenType);
            ICodeNode opNode = ICodeFactory.createICodeNode(nodeType);
            opNode.addChild(rootNode);
            token = nextToken();  
            opNode.addChild(parseFactor(token));
            rootNode = opNode;
            token = currentToken();
            tokenType = token.getType();
        }

        return rootNode;
    }

    /**
     * 解析factor，后续的factor实现会更复杂
     * @param token factor首token.
     * @return factor分析子树根节点
     * @throws Exception
     */
    private ICodeNode parseFactor(Token token)
        throws Exception
    {
        TokenType tokenType = token.getType();
        ICodeNode rootNode = null;
        switch ((PascalTokenType) tokenType) {
            case IDENTIFIER: { //变量
                String name = token.getText().toLowerCase();
                //找不到就相当于变量没定义就用了，报错
                SymTabEntry id = symTabStack.lookup(name);
                if (id == null) {
                    errorHandler.flag(token, IDENTIFIER_UNDEFINED, this);
                    id = symTabStack.enterLocal(name);
                }
                rootNode = ICodeFactory.createICodeNode(VARIABLE);
                rootNode.setAttribute(ID, id);
                id.appendLineNumber(token.getLineNumber());
                token = nextToken();
                break;
            }
            case INTEGER: { //整数
                rootNode = ICodeFactory.createICodeNode(INTEGER_CONSTANT);
                rootNode.setAttribute(VALUE, token.getValue());
                token = nextToken(); 
                break;
            }
            case REAL: {//浮点数
                rootNode = ICodeFactory.createICodeNode(REAL_CONSTANT);
                rootNode.setAttribute(VALUE, token.getValue());
                token = nextToken();  
                break;
            }
            case STRING: {//字符串
                String value = (String) token.getValue();
                rootNode = ICodeFactory.createICodeNode(STRING_CONSTANT);
                rootNode.setAttribute(VALUE, value);
                token = nextToken(); 
                break;
            }
            case NOT: {//反值 !FACTOR
            	//跳过NOT
                token = nextToken(); 
                rootNode = ICodeFactory.createICodeNode(ICodeNodeTypeImpl.NOT);
                //解析NOT后面的Factor
                rootNode.addChild(parseFactor(token));
                break;
            }
            case LEFT_PAREN: {//改变优先级的括号()
            	//跳过左边括号(
                token = nextToken(); 
                rootNode = parseExpression(token);
                //跳过右边括号)
                token = currentToken();
                if (token.getType() == RIGHT_PAREN) {
                    token = nextToken();  // consume the )
                }
                else {
                    errorHandler.flag(token, MISSING_RIGHT_PAREN, this);
                }

                break;
            }

            default: {
                errorHandler.flag(token, UNEXPECTED_TOKEN, this);
                break;
            }
        }

        return rootNode;
    }
}
